{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 提示 + LLM\n",
    "最常见和有价值的组合是：\n",
    "\n",
    "`PromptTemplate` / `ChatPromptTemplate` -> `LLM` / `ChatModel` -> `OutputParser`\n",
    "\n",
    "几乎任何其他链都会使用这个构建块。\n",
    "\n",
    "## PromptTemplate + LLM\n",
    "最简单的组合只是将提示和模型组合在一起，创建一个链，接受用户输入，将其添加到提示中，传递给模型，并返回原始模型输出。\n",
    "\n",
    "注意，您可以在这里随意混合和匹配PromptTemplate/ChatPromptTemplates和LLMs/ChatModels。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "253a764b852f9a49"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "\n",
    "from dotenv import load_dotenv\n",
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "load_dotenv()\n",
    "prompt = ChatPromptTemplate.from_template(\"tell me a joke about {foo}\")\n",
    "llm = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    max_retries=0,\n",
    ")\n",
    "chain = prompt | llm"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:36.523433Z",
     "start_time": "2024-10-29T08:47:36.424842Z"
    }
   },
   "id": "441eaf34fa52600f",
   "execution_count": 28
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='当然，这里有一个关于熊的笑话：\\n\\n为什么熊不喜欢用电脑？\\n\\n因为它们害怕“熊”抱（熊抱 = 拥抱；而电脑可能会有病毒，\"熊\"抱听起来像是“熊病毒”，在中文里玩了个谐音梗）！\\n\\n希望这个笑话能让你会心一笑！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 66, 'prompt_tokens': 26, 'total_tokens': 92, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'qwen-max', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-239280d9-c8e7-43ca-a999-031f55709426-0', usage_metadata={'input_tokens': 26, 'output_tokens': 66, 'total_tokens': 92, 'input_token_details': {}, 'output_token_details': {}})"
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"熊\"})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:42.572583Z",
     "start_time": "2024-10-29T08:47:36.526197Z"
    }
   },
   "id": "41d3524e220392d3",
   "execution_count": 29
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 附加停止系列\n",
    "链可以附加一个或多个停止序列，这些序列将导致链停止并返回。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "cc8bd8d674d68865"
  },
  {
   "cell_type": "markdown",
   "source": [],
   "metadata": {
    "collapsed": false
   },
   "id": "30b9894630a24fc4"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "chain = prompt | llm.bind(stop=[\"\\n\"])"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:42.579370Z",
     "start_time": "2024-10-29T08:47:42.575263Z"
    }
   },
   "id": "2f2fe039991c3984",
   "execution_count": 30
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='当然，这里有一个关于熊的笑话：', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 26, 'total_tokens': 35, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'qwen-max', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-f1a375b5-5fe8-4d80-87b7-327dadeee639-0', usage_metadata={'input_tokens': 26, 'output_tokens': 9, 'total_tokens': 35, 'input_token_details': {}, 'output_token_details': {}})"
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"熊\"})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:43.509675Z",
     "start_time": "2024-10-29T08:47:42.583345Z"
    }
   },
   "id": "1fe7ff8c678b5b24",
   "execution_count": 31
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 附加函数调用信息\n",
    "链可以附加一个或多个函数调用信息，这些信息将导致链返回函数调用信息。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "b843ddf37b99b763"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "functions = [\n",
    "    {\n",
    "        \"name\": \"笑话\",\n",
    "        \"description\": \"一个笑话\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"setup\": {\"type\": \"string\", \"description\": \"笑话的开头\"},\n",
    "                \"punchline\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"笑话的结尾\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"setup\", \"punchline\"],\n",
    "        },\n",
    "    }\n",
    "]\n",
    "\n",
    "chain = prompt | llm.bind(functions=functions, function_call={\"name\": \"笑话\"})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:43.518944Z",
     "start_time": "2024-10-29T08:47:43.512602Z"
    }
   },
   "id": "3460d6b004020696",
   "execution_count": 32
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"setup\": \"Why did the bear dissolve in water?\", \"punchline\": \"Because it was a polar bear!\"}', 'name': '笑话'}, 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 27, 'prompt_tokens': 206, 'total_tokens': 233, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'qwen-max', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-42e65f2c-af9f-4291-a5da-d2e3ca788208-0', usage_metadata={'input_tokens': 206, 'output_tokens': 27, 'total_tokens': 233, 'input_token_details': {}, 'output_token_details': {}})"
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"熊\"}, config={})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:47.149103Z",
     "start_time": "2024-10-29T08:47:43.522641Z"
    }
   },
   "id": "1dfd7596604e32a0",
   "execution_count": 33
  },
  {
   "cell_type": "markdown",
   "source": [
    "## PromptTemplate + LLM + OutputParser\n",
    "可以添加一个输出解析器，将原始的LLM/ChatModel输出轻松转换为更可操作的格式\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "d7975ca1b959c5e8"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "chain = prompt | llm | StrOutputParser()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:47.155494Z",
     "start_time": "2024-10-29T08:47:47.151883Z"
    }
   },
   "id": "b8fb74ce72afced0",
   "execution_count": 34
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'当然，这里有一个关于熊的笑话：\\n\\n为什么熊不喜欢用电脑？\\n\\n因为它害怕“熊”抱病毒！（“熊”抱是中文里拥抱的意思，这里用来双关电脑病毒。）\\n\\n希望这个笑话能让你笑一笑！'"
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"熊\"})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:47:52.429719Z",
     "start_time": "2024-10-29T08:47:47.157539Z"
    }
   },
   "id": "e6413296c021b577",
   "execution_count": 35
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 函数输出解析器\n",
    "函数输出解析器可以轻松地解析函数调用输出。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "76e4546798a4e6a5"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "from langchain_core.output_parsers.openai_functions import JsonOutputFunctionsParser\n",
    "\n",
    "chain = prompt | llm.bind(functions=functions, function_call={\"name\": \"笑话\"}) | JsonOutputFunctionsParser()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:48:40.753113Z",
     "start_time": "2024-10-29T08:48:40.745568Z"
    }
   },
   "id": "190110c8f36f40a4",
   "execution_count": 39
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'setup': 'Why did the bear dissolve in water?',\n 'punchline': 'Because it was a polar bear!'}"
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"熊\"})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:48:47.675222Z",
     "start_time": "2024-10-29T08:48:43.474539Z"
    }
   },
   "id": "7db42ce302bcf986",
   "execution_count": 40
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'setup': 'Why did the bear dissolve in water?',\n 'punchline': 'Because it was a polar bear!'}"
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain = prompt | llm.bind(functions=functions, function_call={\"name\": \"笑话\"}) | JsonOutputFunctionsParser(\n",
    "    key_name=\"setup\")\n",
    "chain.invoke({\"foo\": \"熊\"})\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:48:51.684529Z",
     "start_time": "2024-10-29T08:48:47.679102Z"
    }
   },
   "id": "a087cc0b41a64767",
   "execution_count": 41
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 简化输入\n",
    "为了使调用更简单，我们可以添加一个RunnableParallel来为我们创建提示输入字典：\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "809d2f4485affa7d"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'setup': '为什么熊不喜欢在森林里玩扑克牌？',\n 'punchline': '因为那里到处都是作弊者（cheetahs，此处为英文谐音梗，cheetahs意为猎豹，发音与cheaters相近）！'}"
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "chain = (\n",
    "        # RunnableParallel(foo=RunnablePassthrough())等价于{\"foo\": RunnablePassthrough()}\n",
    "        RunnableParallel(foo=RunnablePassthrough())\n",
    "        | prompt | llm.bind(functions=functions, function_call={\n",
    "    \"name\": \"笑话\"}) | JsonOutputFunctionsParser(key_name=\"setup\"))\n",
    "chain.invoke(\"熊\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-29T08:51:46.475006Z",
     "start_time": "2024-10-29T08:51:40.468748Z"
    }
   },
   "id": "5e6fe0eb96fed97b",
   "execution_count": 42
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false
   },
   "id": "b7f0bd5c78b6912e"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
